import datetime
import logging

from flask import current_app
from flask import g
from flask import jsonify
from flask import request
from flask import session

from iHome import constants, db
from iHome import redis_store
from iHome.api_1_0 import api
from iHome.models import Area, House, Facility, HouseImage, Order
from iHome.utils.commons import login_required
from iHome.utils.image_storage import storage_image
from iHome.utils.response_code import RET


# 我的发布列表
@api.route('/user/houses')
@login_required
def get_user_house_list():
    """
    获取用户房屋列表
    1. 获取当前登录用户id
    2. 查询数据
    :return:
    """
    user_id = g.user_id

    try:
        houses = House.query.filter(House.user_id == user_id).all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询数据失败")

    houses_dict = []
    for house in houses:
        houses_dict.append(house.to_basic_dict())
    return jsonify(errno=RET.OK, errmsg="OK", data=houses_dict)






@api.route('/areas')
def get_areas_info():
    """
    查询城区信息
    :return:
    """

    # 先从缓存中去取，如果缓存中没有，再去数据库中取
    try:
        areas = redis_store.get('area_info')
    except Exception as e:
        logging.error(e)
        areas = None

    # 如果不为空，做查询操作
    if areas:
        return jsonify(errno=RET.OK, errmsg='获取成功', data=eval(areas))

    # 从数据库取
    try:
        areas = Area.query.all()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='获取城区信息失败')

    # 因为查询出来全是对象实例，不能直接进行 JSON 转化，所以定义一个数组保存所有对象的字典信息
    areas_list = []
    for area in areas:
        areas_list.append(area.to_dict())

    # 存储json_areas数据到redis缓存中
    try:
        redis_store.setex('area_info', constants.AREA_INFO_REDIS_EXPIRES, areas_list)
    except Exception as e:
        logging.error(e)

    return jsonify(errno=RET.OK, errmsg='获取成功', data=areas_list)

@api.route('/houses', methods=['POST'])
@login_required
def save_new_house():
    """
    前端发送过来的json数据
    {
        "title":"",
        "price":"",
        "area_id":"1",
        "address":"",
        "room_count":"",
        "acreage":"",
        "unit":"",
        "capacity":"",
        "beds":"",
        "deposit":"",
        "min_days":"",
        "max_days":"",
        "facility":["7","8"]
    }

    :return:
    """

    # 获取用户id
    user_id = g.user_id

    # 获取数据
    json_dict = request.get_json()
    if not json_dict:
        return jsonify(errno=RET.PARAMERR, errmsg='请输入参数')

    title = json_dict.get('title')
    price = json_dict.get('price')
    address = json_dict.get('address')
    area_id = json_dict.get('area_id')
    room_count = json_dict.get('room_count')
    acreage = json_dict.get('acreage')
    unit = json_dict.get('unit')
    capacity = json_dict.get('capacity')
    beds = json_dict.get('beds')
    deposit = json_dict.get('deposit')
    min_days = json_dict.get('min_days')
    max_days = json_dict.get('max_days')

    if not all([title, price, address, area_id, room_count, acreage, unit, capacity, beds, deposit, min_days, max_days]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')

    try:
        price = int(float(price) * 100)
        deposit = int(float(deposit) * 100)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg='参数错误')

    # 设置数据到模型
    house = House()
    house.user_id = user_id
    house.area_id = area_id
    house.title = title
    house.price = price
    house.address = address
    house.room_count = room_count
    house.acreage = acreage
    house.unit = unit
    house.capacity = capacity
    house.beds = beds
    house.deposit = deposit
    house.min_days = min_days
    house.max_days = max_days

    # 设置设施信息
    facility = json_dict.get('facility')
    if facility:
        facilities = Facility.query.filter(Facility.id.in_(facility)).all()
        house.facilities = facilities

    try:
        db.session.add(house)
        db.session.commit()
    except Exception as e:
        logging.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg='保存房屋信息失败')

    return jsonify(errno=RET.OK, errmsg='OK', data={'house_id': house.id})


@api.route('/houses/<int:house_id>/images', methods=['POST'])
@login_required
def upload_house_pic(house_id):
    """
    上传房源图片
    :param house_id: 房源id
    :return:
    """

    # 取到图片文件
    image_file = request.files.get('house_image')
    if not image_file:
        return jsonify(errno=RET.PARAMERR, errmsg="未选择图片")

    # 尝试查询房屋数据
    try:
        house = House.query.filter_by(id=house_id).first()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询房屋数据失败')
    if not house:
        return jsonify(errno=RET.NODATA, errmsg='未查询到对应房屋')

    # 使用七牛上传图片
    image_data = image_file.read()
    try:
        image_name = storage_image(image_data)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg="上传图片到七牛失败")

    # 判断房屋是否有主图片，如果没有，则设置
    if not house.index_image_url:
        house.index_image_url = image_name
        db.session.add(house)

    # 生成房屋图片模型并保存至数据数据库
    house_image = HouseImage()
    house_image.house_id = house_id
    house_image.url = image_name

    try:
        db.session.add(house_image)
        db.session.commit()
    except Exception as e:
        logging.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存房屋图片失败")

    # 返回数据
    image_url = constants.QINIU_DOMIN_PREFIX + image_name
    return jsonify(errno=RET.OK, errmsg='OK', data={'url': image_url})



@api.route('/houses/<house_id>')
def house_detail(house_id):
    """
    房屋详情
    :param house_id:
    :return:
    """
    # 前端在房屋详情页面展示时，如果浏览页面的用户不是该房屋的房东，则展示预定按钮，否则不展示，
    # 所以需要后端返回登录用户的user_id
    # 尝试获取用户登录的信息，若登录，则返回给前端登录用户的user_id，否则返回user_id=-1

    # 判断参数是否有值
    if not house_id:
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')

    user_id = session.get('user_id', -1)
    # 先从redis缓存中取
    try:
        house_info = redis_store.get('house_info_'+house_id)
    except Exception as e:
        house_info = None
        logging.error(e)

    # 如果有值,那么返回数据
    if house_info:
        logging.info('get house info from redis')
        return jsonify(errno=RET.OK, errmsg='OK', data={"user_id": user_id, "house": eval(house_info)})

    # 没有从缓存中取到,查询数据)库
    house = House.query.filter_by(id=house_id).first()
    if not house:
        return jsonify(errno=RET.NODATA, errmsg='未查询到房屋信息')

    # 将数据缓存到redis中
    house_dict = house.to_full_dict()
    try:
        redis_store.setex('house_info_'+house_id, constants.HOUSE_DETAIL_REDIS_EXPIRE_SECOND, house_dict)
    except Exception as e:
        logging.error(e)
    # 返回数据
    return jsonify(errno=RET.OK, errmsg='OK', data={"user_id": user_id, "house": house_dict})


@api.route('/houses/index')
def house_index():
    """
    获取首页推荐房屋信息轮播
    :return:
    """

    # 先从redis中加载数据
    try:
        house_info = redis_store.get('home_page_house_info')
        print(house_info)
    except Exception as e:
        house_info = None
        logging.error(e)
    if house_info:
        return jsonify(errno=RET.OK, errmsg='OK', data=eval(house_info))

    # 从数组库中加载数据
    try:
        houses = House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_HOUSES).all()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='数据查询失败')

    # 如果数据库中没有数据
    if not houses:
        return jsonify(errno=RET.NODATA, errmsg='未查询到数据')

    # 拼接到数组中
    houses_dict = []
    for house in houses:
        houses_dict.append(house.to_basic_dict())

    # 缓存到redis中
    try:
        redis_store.setex('home_page_house_info', constants.HOME_PAGE_DATA_REDIS_EXPIRES, houses_dict)
    except Exception as e:
        logging.error(e)

    # 返回数据alembic_version
    return jsonify(errno=RET.OK, errmsg='OK', data=houses_dict)

@api.route('/houses')
def house_list():
    """
    首页搜索房屋
    :return:
    """
    # 获取所有的参数
    args = request.args
    area_id = args.get('aid', '')
    start_date_str = args.get('sd', '')
    end_date_str = args.get('ed', '')
    # booking(订单量), price-inc(低到高), price-des(高到低),
    sort_key = args.get('sk', 'new')
    page = args.get('p', '1')

    try:
        page = int(page)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")

    # 日期转换
    try:
        start_date = None
        end_date = None
        if start_date_str:
            start_date = datetime.datetime.strptime(start_date_str, '%Y-%m-%d')
        if end_date_str:
            end_date = datetime.datetime.strptime(end_date_str, '%Y-%m-%d')
        # 如果开始时间大于或者等于结束时间，就报错
        if start_date and end_date:
            assert start_date < end_date, Exception("开始时间大于结束时间")
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.PARAMERR, errmsg="参数错误")

    # 从缓存中取出房屋列表
    try:
        redis_key = "houses_%s_%s_%s_%s" % (start_date_str, end_date_str, area_id, sort_key)
        response_data = redis_store.hget(redis_key, page)
        if response_data:
            return jsonify(errno=RET.OK, errmsg="OK", data=eval(response_data))
    except Exception as e:
        current_app.logger.error(e)

    # 查询数据
    house_query = House.query

    filters = []
    # 判断是否传入城区id
    if area_id:
        filters.append(House.area_id == area_id)

    # 过滤已预订的房屋
    conflict_order = None
    try:
        if start_date and end_date:
            conflict_order = Order.query.filter(Order.begin_date <= end_date, Order.end_date >= start_date).all()
        elif start_date:
            conflict_order = Order.query.filter(Order.end_date >= start_date).all()
        elif end_date:
            conflict_order = Order.query.filter(Order.begin_date <= end_date).all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询数据错误")

    if conflict_order:
        # 取到冲突订单的房屋id
        conflict_house_id = [order.house_id for order in conflict_order]
        # 添加条件：查询出房屋不包括冲突订单中的房屋id
        filters.append(House.id.notin_(conflict_house_id))

    # 根据筛选条件进行排序
    if sort_key == "booking":
        house_query = house_query.filter(*filters).order_by(House.order_count.desc())
    elif sort_key == "price-inc":
        house_query = house_query.filter(*filters).order_by(House.price.asc())
    elif sort_key == "price-des":
        house_query = house_query.filter(*filters).order_by(House.price.desc())
    else:
        house_query = house_query.filter(*filters).order_by(House.create_time.desc())

    # 进行分页
    paginate = house_query.paginate(int(page), constants.HOUSE_LIST_PAGE_CAPACITY, False)
    # 取到当前页数据
    houses = paginate.items
    # 取到总页数
    total_page = paginate.pages
    # 将查询结果转成字符串
    houses_dict = []
    for house in houses:
        houses_dict.append(house.to_basic_dict())

    response_data = {"total_page": total_page, "houses": houses_dict}
    try:
        redis_key = "houses_%s_%s_%s_%s" % (start_date_str, end_date_str, area_id, sort_key)
        # 创建redis管道, 支持多命令事务
        pipe = redis_store.pipeline()
        # 开启事务
        pipe.multi()
        # 设置数据
        pipe.hset(redis_key, page, response_data)
        # 设置过期时间
        pipe.expire(redis_key, constants.HOUSE_LIST_REDIS_EXPIRES)
        # 提交事务
        pipe.execute()
    except Exception as e:
        current_app.logger.error(e)

    return jsonify(errno=RET.OK, errmsg='请求成功', data=response_data)
